use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::Mutex;

use crate::conn::async_conn::AsyncConnection;
use crate::conn::{ConnectionConfig, ConnectionState};
use crate::error::Result;
use crate::query::{QueryResult, Statement, Transaction};
use crate::types::Value;

/// A client for interacting with Vertica database
#[derive(Debug, Clone)]
pub struct Client {
    /// 用于与 Vertica 数据库进行异步连接的连接对象，使用 Arc 和 Mutex 实现多线程安全
    pub connection: Arc<Mutex<AsyncConnection>>,
}

impl Client {
    /// Create a new client from connection configuration
    pub async fn new(config: ConnectionConfig) -> Result<Self> {
        let connection = AsyncConnection::new(config).await?;
        Ok(Self {
            connection: Arc::new(Mutex::new(connection)),
        })
    }

    /// Create a new client from URL
    pub async fn from_url(url: &str) -> Result<Self> {
        let config = ConnectionConfig::from_url(url)?;
        Self::new(config).await
    }

    /// Query the database
    pub async fn query(&self, sql: &str) -> Result<QueryResult> {
        let mut conn = self.connection.lock().await;
        let result = conn.simple_query(sql).await?;
        Ok(result)
    }

    /// Execute a SQL statement (INSERT, UPDATE, DELETE, etc.)
    pub async fn execute(&self, sql: &str) -> Result<u64> {
        let mut conn = self.connection.lock().await;
        let result = conn.simple_query(sql).await?;
        Ok(result.affected_rows())
    }

    /// Prepare a SQL statement
    pub async fn prepare(&self, query: &str) -> Result<Statement> {
        let stmt_id = (0..16)
            .map(|_| fastrand::alphanumeric())
            .collect::<String>();
        
        let mut conn = self.connection.lock().await;
        conn.prepare(&stmt_id, query).await?;
        
        Ok(Statement::new(
            self.clone(),
            stmt_id,
            query.to_string(),
        ))
}

    /// Execute a query with parameters
    pub async fn execute_with_params(&self, query: &str, params: &[Value]) -> Result<u64> {
        let stmt = self.prepare(query).await?;
        stmt.execute(params).await
    }

    /// Execute a prepared statement with parameters
    pub async fn execute_prepared(&self, stmt: &Statement, params: &[Value]) -> Result<QueryResult> {
        let mut conn = self.connection.lock().await;
        conn.execute_prepared(&stmt.sql(), params).await
    }

    /// Begin a transaction
    pub async fn transaction(&self) -> Result<Transaction> {
        let mut conn = self.connection.lock().await;
        conn.simple_query("BEGIN").await?;
        Ok(Transaction::new(self.clone()))
    }

    /// Get connection state
    pub async fn connection_state(&self) -> ConnectionState {
        let conn = self.connection.lock().await;
        conn.state()
    }

    /// Check if connection is ready
    pub async fn is_ready(&self) -> bool {
        let conn = self.connection.lock().await;
        conn.is_ready()
    }

    /// Get server parameters
    pub async fn server_parameters(&self) -> HashMap<String, String> {
        let conn = self.connection.lock().await;
        conn.parameters().clone()
    }

    /// Ping the server to check connectivity
    pub async fn ping(&self) -> Result<()> {
        self.query("SELECT 1").await?;
        Ok(())
    }

    /// Execute a batch of queries
    pub async fn execute_batch(&self, queries: &[&str]) -> Result<Vec<QueryResult>> {
        let mut results = Vec::new();
        for query in queries {
            results.push(self.query(query).await?);
        }
        Ok(results)
    }

    /// Close the connection
    pub async fn close(&self) -> Result<()> {
        let mut conn = self.connection.lock().await;
        conn.close().await
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    async fn test_client_creation() {
        let config = ConnectionConfig::from_url("vertica://user:pass@localhost:5433/db").unwrap();
        let client = Client::new(config).await;
        assert!(client.is_ok());
    }

    #[tokio::test]
    async fn test_client_from_url() {
        let client = Client::from_url("vertica://user:pass@localhost:5433/db").await;
        assert!(client.is_ok());
    }

    #[tokio::test]
    async fn test_client_operations() {
        let client = Client::from_url("vertica://user:pass@localhost:5433/db").await;
        assert!(client.is_ok());
        
        let client = client.unwrap();
        assert!(!client.is_ready().await);
    }
}